/*            Copyright(c) 2006-2008 Dave Sexton           *
 * http://www.codeplex.com/DocProject                      *
 * http://www.codeplex.com/DocProject/Project/License.aspx *
 ***********************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Drawing.Design;

namespace DaveSexton.DocProject
{
	public abstract class DocProjectOptionsEditorBase<TOptions> : UITypeEditor
		where TOptions : DocProjectOptions
	{
		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="DocProjectOptionsEditorBase" /> class.
		/// </summary>
		protected DocProjectOptionsEditorBase()
		{
		}
		#endregion

		#region Methods
		protected abstract bool HandleChanges(TOptions options, object result);

		protected virtual TOptions GetOptions(ITypeDescriptorContext context, IServiceProvider provider, object value)
		{
			return (context == null) ? null : context.Instance as TOptions;
		}

		protected abstract object GetValue(TOptions options, object result);

		protected abstract string GetPropertyName(TOptions options, ITypeDescriptorContext context, IServiceProvider provider, object value);

		protected abstract object EditValue(TOptions options, ITypeDescriptorContext context, IServiceProvider provider, object value);

		protected virtual void BeforeEdit(TOptions options, ITypeDescriptorContext context, IServiceProvider provider, object value)
		{
		}

		protected virtual bool AfterEdit(TOptions options, ITypeDescriptorContext context, IServiceProvider provider, object value, Exception ex)
		{
			return false;
		}

		[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.LinkDemand, Name = "FullTrust")]
		public sealed override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
		{
			if (context == null)
				throw new ArgumentNullException("context");

			TOptions options = GetOptions(context, provider, value);

			if (options == null)
				return null;

			BeforeEdit(options, context, provider, value);

			bool afterEditInvoked = false;

			try
			{
				object result = EditValue(options, context, provider, value);

				if (HandleChanges(options, result))
				{
					object newValue = GetValue(options, result);

					options.NotifyChanged(GetPropertyName(options, context, provider, value), value, newValue);

					value = newValue;
				}
			}
			catch (Exception ex)
			{
				if (!AfterEdit(options, context, provider, value, ex))
					throw;
				else
					afterEditInvoked = true;
			}

			if (!afterEditInvoked)
				AfterEdit(options, context, provider, value, null);

			return value;
		}
		#endregion
	}
}
